import sys

from PySide6.QtWidgets import QWidget, QVBoxLayout, QLabel, QCheckBox, QPushButton, QHBoxLayout, QApplication, QLayout, QDialog
from PySide6.QtCore import Qt, Signal, QThread, Slot

from openpyxl import load_workbook
from pathlib import Path

from api import api_convert, api_read, api_ui

class BalanceSheetUi(QDialog):
    rel_tip_signal = Signal(str, str)
    show_msg_signal = Signal(dict)
    log_signal = Signal(str, str)

    def __init__(self, group_box_style: str):
        super().__init__()
        self.setWindowTitle('科目余额表转换')
        self.all_checkbox = []          # 存储所有的checkbox
        self.resize(700, 480)
        self.setup_ui()

    def checkbox_ui(self, label_text: str, txt_tuple: tuple, checked_index: int = 0):
        box_list = []
        layout = QHBoxLayout()
        label = QLabel(f'{label_text}')
        label.setFixedWidth(120)
        label.setAlignment(Qt.AlignCenter)
        layout.addWidget(label)
        for txt in txt_tuple:
            checkbox = QCheckBox(txt)
            layout.addWidget(checkbox)
            box_list.append(checkbox)
        box_list[checked_index].setChecked(True)
        self.all_checkbox.append(box_list)
        return layout, box_list

    @Slot()
    def ckbox_clicked(self):
        # 设置互斥关系
        # 通过 self.sender() 获取发送信号的对象，再通过 self.all_checkbox 找到其他关联的 checkbox
        current_checkbox = self.sender()
        for box_list in self.all_checkbox:
            if current_checkbox in box_list:
                current_boxlist = box_list
        length = len(current_boxlist)
        state = current_checkbox.isChecked()
        if state:
            for box in current_boxlist:
                if box == current_checkbox:
                    continue
                box.setChecked(False)
        else:
            if length == 2:
                # exclusive_checkbox = box_list[abs(box_list.index(current_checkbox) - 1)]        # 两种写法
                exclusive_checkbox, = [x for x in current_boxlist if x != current_checkbox]
                exclusive_checkbox.setChecked(True)

        self.receivable_widget.show() if self.receivable_ckbox.isChecked() else self.receivable_widget.hide()
        self.unit_info_widget.show() if self.private_enterprise_ckbox.isChecked() else self.unit_info_widget.hide()


    @Slot()
    def convert_file_ckbox_clicked(self):
        current_box = self.sender()
        if current_box.isChecked():
            file_type = current_box.text()
            self.to_file_label.setText(file_type)
            self.to_file_edit.setToolTip(f'请选择{file_type}')

    @Slot(bool)
    def receive_decision(self, choice):
        """槽函数，用于接收用户选择，并发送给子线程"""
        self.send_to_subthread_signal.emit(choice)
        
    def check_input(self):
        if self.yuan_checkbox.isChecked() or self.wanyuan_checkbox.isChecked():
            if (self.receivable_ckbox.isChecked() and (self.short_receivable_ckbox.isChecked() or self.long_receivable_ckbox.isChecked())) or\
                (self.private_enterprise_ckbox.isChecked() and self.unit_info_edit.text()):
                if self.balance_edit.text() and self.to_file_edit.text():
                    self.run_thread()
                else:
                    self.show_msg_signal({'message': '请选择文件路径'})
            else:
                self.show_msg_signal({'message': '请选择转换文件类型'})
        else:
            self.show_msg_signal({'message': '请选择单位'})

    @Slot(int)
    def after_finish(self, sign: int):
        self.start_btn.setText('开始')
        self.start_btn.setEnabled(True)
        if sign:
            self.rel_tip_signal.emit('完成', 'green')
            self.rel_tip_signal.emit('-'*99, 'green')

    def run_thread(self):
        self.start_btn.setEnabled(False)
        self.start_btn.setText('执行中')
        self.rel_tip_signal.emit('开始', 'blue')

        unit = 'yuan' if self.yuan_checkbox.isChecked() else 'wanyuan'
        if self.receivable_ckbox.isChecked():
            if self.short_receivable_ckbox.ischecked():
                file_type = '应付账款'
            else:
                file_type = '长期应付账款'
        else:
            file_type = '对民营企业支付情况统计表'
        balance_path = self.balance_edit.text()
        to_file_path = self.to_file_edit.text()
        unit_info_path = self.unit_info_edit.text()

        self.thread = Balance2othersThread(unit, file_type, balance_path, to_file_path, unit_info_path)
        self.thread.tip_signal.connect(self.rel_tip_signal)
        self.thread.finished_signal.connect(self.after_finish)
        self.thread.start()

    def setup_ui(self):
        # 设置单位，元/万元
        unit_layout, (self.yuan_checkbox, self.wanyuan_checkbox) = self.checkbox_ui('单位', ('元', '万元'))
        self.yuan_checkbox.stateChanged.connect(self.ckbox_clicked)
        self.wanyuan_checkbox.stateChanged.connect(self.ckbox_clicked)
        unit_layout.addWidget(self.yuan_checkbox)
        unit_layout.addWidget(self.wanyuan_checkbox)
        # 暂时用不到单位切换，禁用
        self.yuan_checkbox.setEnabled(False)
        self.wanyuan_checkbox.setEnabled(False)

        # 设置转换文件
        convert_file_layout, (self.receivable_ckbox, self.private_enterprise_ckbox) = self.checkbox_ui('转换文件', ('应收应付款项统计表', '对民营企业支付情况统计表'))
        self.receivable_ckbox.stateChanged.connect(self.convert_file_ckbox_clicked)
        self.receivable_ckbox.stateChanged.connect(self.ckbox_clicked)
        self.private_enterprise_ckbox.stateChanged.connect(self.convert_file_ckbox_clicked)
        self.private_enterprise_ckbox.stateChanged.connect(self.ckbox_clicked)
        convert_file_layout.addWidget(self.receivable_ckbox)

        # 应收应付款项统计表，选择应付账款或长期应付款
        receivable_layout, (self.short_receivable_ckbox, self.long_receivable_ckbox) = self.checkbox_ui('应付款项', ('应付账款', '长期应付款'))
        self.short_receivable_ckbox.stateChanged.connect(self.ckbox_clicked)
        self.long_receivable_ckbox.stateChanged.connect(self.ckbox_clicked)
        self.receivable_widget = QWidget()
        self.receivable_widget.setLayout(receivable_layout)
        receivable_layout.setContentsMargins(0, 0, 0, 0)            # 避免外边距影响布局。将 layout 添加到 widget 后，边距会改变
       
        # 选择科目余额表的路径
        balance_layout, balance_label, self.balance_edit, balance_button = api_ui.file_path_ui('科目余额表', False)
        
        # 选择转换文件的路径
        to_file_layout, self.to_file_label, self.to_file_edit, to_file_button = api_ui.file_path_ui('应收应付款项统计表', False)

        # 选择往来单位信息统计表的路径
        unit_info_layout, unit_info_label, self.unit_info_edit, unit_info_button = api_ui.file_path_ui('往来单位信息统计表', False)
        self.unit_info_widget = QWidget()
        self.unit_info_widget.hide()
        self.unit_info_widget.setLayout(unit_info_layout)
        unit_info_layout.setContentsMargins(0, 0, 0, 0)            # 避免外边距影响布局。将 layout 添加到 widget 后，边距会改变

        # 开始按钮
        self.start_btn = QPushButton('开始')
        self.start_btn.setFixedWidth(60)
        self.start_btn.clicked.connect(self.check_input)

        # 宏观布局
        main_layout = QVBoxLayout()
        main_layout.setAlignment(Qt.AlignCenter)
        main_layout.setSizeConstraint(QLayout.SetNoConstraint)
        self.setLayout(main_layout)
        main_layout.addLayout(unit_layout)
        main_layout.addLayout(convert_file_layout)
        main_layout.addWidget(self.receivable_widget)
        main_layout.addLayout(balance_layout)
        main_layout.addLayout(to_file_layout)
        main_layout.addWidget(self.unit_info_widget)
        main_layout.addWidget(self.start_btn, alignment=Qt.AlignCenter)
        
class Balance2othersThread(QThread):
    tip_signal = Signal(str, str)
    finished_signal = Signal(int)

    def __init__(self, unit: str, file_type: str, balance_path: str, to_file_path: str, unit_info_path: str):
        super().__init__()
        self.unit = unit
        self.file_type = file_type
        self.balance_path = Path(balance_path)
        self.to_file_path = Path(to_file_path)
        self.unit_info_path = Path(unit_info_path)

        self.balance_files = None
        self.balance_content = None
    
    @Slot(str, object)
    def get_value(self, key, value):
        exec(f'{key} = value')

    def run(self):
        if self.file_type == '应付账款' or '长期应付账款':
            self.receivable()

    def receivable(self):
        # 向报表中填充数据。因为应收应付款项统计表中需要填单位性质等信息，所以以统计表为主，在科目余额表中检索数据
        wb = load_workbook(self.to_file_path, data_only=True)
        ws = wb.active
        max_row = ws.max_row          # 获取工作表中有数据的区域
        for row in range(5, max_row):               # 清空需要填充的区域
            if self.file_type == '应付账款':
                # 应付账款
                ws['J' + str(row)].value = None
                ws['R' + str(row)].value = None
            else:
                # 长期应付款
                ws['K' + str(row)].value = None
                ws['U' + str(row)].value = None

        source_data = api_read.read_unit_by_balance(self.balance_path)
        for num, d in source_data.copy().items():
            name, account = d.values()
            for the_cell in ws['B'][4:]:        # 这里需要使用“[4:]”，否则会跳过第一行。因为这里是列表索引，不是工作表中的行号。
                if the_cell.value == name:
                    self.fill_in(the_cell, account)
                    source_data.pop(num)
        # 处理报表里没有，但科目余额表里有的单位
        if source_data:
            self.tip_signal.emit(f'有 {len(source_data)} 个往来单位没有在报表中，正在添加', 'yellow')
            # 因为max_row并不可靠，所以需要遍历所有行，找到最后一行
            for row in range(5, max_row+2):
                if ws['B' + str(row)].value == None:      # 找到最后一行
                    last_row = row
                    break
            for d in source_data.values():
                name, account = d.values()
                if account == 0:
                    self.tip_signal.emit(f'{name} 的余额为 0，已自动忽略', 'yellow')
                    continue
                the_cell = ws['B' + str(last_row)]
                the_cell.value = name
                self.fill_in(the_cell, account)
                last_row += 1
                self.tip_signal.emit(f'已添加 {name}', 'green')
        # 将工程款改为 220201|工程款 ，防止导入识别错误
        for row in range(5, max_row+1):
            the_cell = ws['G' + str(row)]
            if the_cell.value == '工程款':
                the_cell.value = '220201|工程款'
        wb.save(self.to_file_path)
        
    def fill_in(self, cell, account):
        # 以单位名称所在单元格定位余额所在单元格
        if self.file_type == '应付账款':
            # 应付账款
            cell.offset(column=8).value = api_convert.convert_to_wan(account)       # 将当前单元格向右移动8列，并写入余额
            cell.offset(column=16).value = api_convert.convert_to_wan(account)
        else:
            # 长期应付款
            cell.offset(column=9).value = api_convert.convert_to_wan(account)
            cell.offset(column=19).value = api_convert.convert_to_wan(account)
    
    def private_enterprise(self):
        unit_contents = api_read.read_unit_by_balance(self.balance_path, 'E')
        unit_contents = list(unit_contents.values())
        for unit_content in unit_contents:
            if unit_content['account'] in (0, '0', '0.00'):
                unit_contents.remove(unit_content)
            else:
                self.tip_signal.emit(unit_content['account'], 'yellow')

        unit_with_payment = unit_contents       # [{'name': 'xxx', 'account': '123'}, ...]
        all_ammount = sum([unit['account'] for unit in unit_with_payment])
        self.tip_signal.emit(f'本月付款：{unit_with_payment}，金额合计为：{all_ammount}', 'green')

        wb = load_workbook(self.unit_info_path)
        ws = wb.active
        all_unit = {}
        for cell in ws['B']: all_unit[cell.value] = cell.row
        small_enterprise_payment = {}
        for unit in unit_with_payment:
            unit_name, account = unit.values()
            if unit_name in all_unit:
                enterprise_size = ws[f'D{all_unit[unit_name]}'].value
                if enterprise_size in ('中型', '小型', '微型'):
                    small_enterprise_payment[unit_name] = account
            else:
                self.tip_signal.emit(f'{unit_name} 不在汇总表中，请检查', 'red')

        all_ammount = sum(list(small_enterprise_payment.values()))
        self.tip_signal.emit(f'中小微企业：{small_enterprise_payment}，金额合计为：{all_ammount}', 'green')

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ui = BalanceSheetUi()
    ui.show()
    sys.exit(app.exec())


